package config

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
)

type PoolConfig struct {
	FeMaxConns     int
	FeUser         string
	FePasswd       string
	FeDbName       string
	PortalList     []string /*ip:port的列表，如["192.168.0.12:5432", "192.168.0.13:5432"]*/
	portalIndex    uint32
	BeConns        int
	BeUser         string
	BePasswd       string
	BeDbName       string
	BeConnLifeTime int
	MsgBufSize     int
	Lock           sync.Mutex
}

var g_globalConf = make(map[string]string, 4)
var g_poolConf = make(map[string]*PoolConfig)

func (cfg *PoolConfig) GetNextPortal() string {
	var idx uint32
	var count = uint32(len(cfg.PortalList))
	idx = atomic.AddUint32(&cfg.portalIndex, 1)
	return cfg.PortalList[idx%count]
}

func inArray(strArray []string, key string) bool {
	var item string
	for _, item = range strArray {
		if item == key {
			return true
		}
	}
	return false
}

func LoadConfig(configFile string) error {

	confFp, err := os.Open(configFile)
	if err != nil {
		return err
	}
	defer func() { confFp.Close() }()

	buff := bufio.NewReader(confFp)

	var key string
	var val string
	var lineno int = 0
	var poolConf *PoolConfig
	var idx int
	var ok bool
	var poolMapById = make(map[string]*PoolConfig)

	for {
		line, err := buff.ReadString('\n')
		lineno++

		if err != nil || io.EOF == err {
			break
		}

		line = strings.TrimSpace(line)
		if strings.HasPrefix(line, "#") {
			continue
		}

		if len(line) < 3 {
			continue
		}

		idx = strings.Index(line, "=")
		if len(line) <= 1 {
			continue
		}
		if idx < 0 {
			return fmt.Errorf("Invalid line(no '='): %s ", line)
		}

		key = line[0:idx]
		key = strings.TrimSpace(key)

		val = line[idx+1:]

		// 把一行后的 以#的注释去掉
		sharpPos := strings.Index(val, "#")
		if sharpPos >= 0 {
			val = val[:sharpPos]
		}
		val = strings.TrimSpace(val)

		if !strings.HasPrefix(key, "pool.") {
			g_globalConf[key] = val
			continue
		}

		items := strings.Split(key, ".")
		if len(items) != 3 {
			return fmt.Errorf("Invalid item(%s) in %d line: %s", key, lineno, line)
		}

		poolConf, ok = poolMapById[items[1]]
		if !ok { /*如果没有匹配到则添加到map中*/
			poolConf = new(PoolConfig)
			poolConf.MsgBufSize = 65536
			poolMapById[items[1]] = poolConf
		}

		switch strings.ToLower(items[2]) {
		case "fe_max_conns":
			poolConf.FeMaxConns, _ = strconv.Atoi(val)
		case "fe_user":
			poolConf.FeUser = val
		case "fe_passwd":
			poolConf.FePasswd = val
		case "fe_dbname":
			poolConf.FeDbName = val
		case "be_ipport":
			poolConf.PortalList = strings.Split(val, ",")
		case "be_user":
			poolConf.BeUser = val
		case "be_dbname":
			poolConf.BeDbName = val
		case "be_passwd":
			poolConf.BePasswd = val
		case "be_conns":
			poolConf.BeConns, _ = strconv.Atoi(val)
		case "be_conn_life_time":
			poolConf.BeConnLifeTime, _ = strconv.Atoi(val)
		case "msg_buf_size":
			poolConf.MsgBufSize, _ = strconv.Atoi(val)
		default:
			return fmt.Errorf("Invalid item(%s) in %d line: %s", key, lineno, line)
		}

	}

	for _, poolConf := range poolMapById {
		poolName := fmt.Sprintf("%s.%s", poolConf.FeUser, poolConf.FeDbName)
		_, ok := g_poolConf[poolName]
		if ok {
			return fmt.Errorf("pool config error, fe_user.fe_dbname (%s) must unique!", poolName)
		}
		g_poolConf[poolName] = poolConf
	}

	return nil
}

func Get(key string) string {
	return g_globalConf[key]
}

func GetInt(key string) int {
	val := g_globalConf[key]
	b, err := strconv.Atoi(val)
	if err != nil {
		return 0
	}
	return b
}

func GetAllPoolsConfig() map[string]*PoolConfig {
	return g_poolConf
}

/*某个池增加一个后端的数据库*/
func PoolAddBeDb(poolName string, portal string) error {
	pool, ok := g_poolConf[poolName]
	if !ok {
		return errors.New(fmt.Sprintf("pool(%s) not exists!", poolName))
	}
	pool.Lock.Lock()
	defer pool.Lock.Unlock()
	pool.PortalList = append(pool.PortalList, portal)
	return nil
}

/*某个池移除一个后端的数据库*/
func PoolRemoveBeDb(poolName string, portal string) error {
	pool, ok := g_poolConf[poolName]
	if !ok {
		return errors.New(fmt.Sprintf("pool(%s) not exists!", poolName))
	}
	pool.Lock.Lock()
	defer pool.Lock.Unlock()

	j := 0
	for _, v := range pool.PortalList {
		if v != portal {
			pool.PortalList[j] = v
			j++
		}
	}
	pool.PortalList = pool.PortalList[:j]
	return nil
}

func PoolGetBeDbList(poolName string) ([]string, error) {
	pool, ok := g_poolConf[poolName]
	if !ok {
		return nil, errors.New(fmt.Sprintf("pool(%s) not exists!", poolName))
	}
	pool.Lock.Lock()
	defer pool.Lock.Unlock()

	var portalList = make([]string, len(pool.PortalList))
	copy(portalList, pool.PortalList)
	return portalList, nil
}
